#include "game_engine.h"
#include "error.h"
#include "utility.h"

pthread_t       game_engine::ge_thread;
pthread_mutex_t game_engine::maplock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t game_engine::charlock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t game_engine::buildlock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t game_engine::townlock = PTHREAD_MUTEX_INITIALIZER;

map*            game_engine::innermap = NULL;
clist_t         game_engine::charlist;
blist_t         game_engine::buildlist;
tlist_t         game_engine::townlist;

static void* ge_routine(void*){
    util::fps_limiter lmtr;
    srand(time(NULL));

    pthread_setcanceltype( PTHREAD_CANCEL_DEFERRED, NULL );

    clist_t* charlist = game_engine::getChars();
    blist_t* buildlist = game_engine::getBuilds();
    tlist_t* townlist = game_engine::getTowns();

    while(1){
        lmtr.exec();

        for( clist_t::iterator it = charlist->begin(); it != charlist->end(); it++ ){
            character* ch = (*it);
            game_engine::mu_lock( mtx_charlock );{
                ch->execAI(clock());
            game_engine::mu_unlock(mtx_charlock );}
        }

        for( blist_t::iterator it = buildlist->begin(); it != buildlist->end(); it++ ){
            building* bd = (*it);
            game_engine::mu_lock( mtx_buildlock );{
                bd->execAI(clock());
            game_engine::mu_unlock( mtx_buildlock );}
        }

        for( tlist_t::iterator it = townlist->begin(); it != townlist->end(); it++ ){
            town* tw = (*it);
            game_engine::mu_lock( mtx_townlock );{
                tw->execAI(clock());
            game_engine::mu_unlock( mtx_townlock );}
        }

        pthread_testcancel();
    }

    return NULL;
}

void game_engine::init(){
    error::log(error::con_only,"Game Engine launched\n");

    srand(time(NULL));

    pthread_mutex_lock( &maplock );
    file mapfile;
    mapfile.open("resources/map.map","rb");
    innermap = map::loadMap(&mapfile);
    pthread_mutex_unlock( &maplock );

    pthread_create( &ge_thread, NULL, &ge_routine, NULL );

}

void game_engine::release(){
    pthread_cancel( ge_thread );
    pthread_join( ge_thread, NULL );
    error::log(error::con_only,"Game Engine stopped\n");
}

map* game_engine::getMap(){
    return innermap;
}

clist_t* game_engine::getChars(){
    return &charlist;
}

blist_t* game_engine::getBuilds(){
    return &buildlist;
}

tlist_t* game_engine::getTowns(){
    return &townlist;
}

void game_engine::mu_lock(ge_mutex_id mutex){
    if( mutex == mtx_charlock )         pthread_mutex_lock( &charlock );
    else if( mutex == mtx_maplock )     pthread_mutex_lock( &maplock );
    else if( mutex == mtx_buildlock )   pthread_mutex_lock( &buildlock );
    else if( mutex == mtx_townlock )    pthread_mutex_lock( &townlock );
}

void game_engine::mu_unlock(ge_mutex_id mutex){
    if( mutex == mtx_charlock )         pthread_mutex_unlock( &charlock );
    else if( mutex == mtx_maplock )     pthread_mutex_unlock( &maplock );
    else if( mutex == mtx_buildlock )   pthread_mutex_unlock( &buildlock );
    else if( mutex == mtx_townlock )    pthread_mutex_unlock( &townlock );
}

void game_engine::addChar(character* ch){
    charlist.push_back(ch);
}

void game_engine::remChar(character* ch){}

void game_engine::addTown(town* tw){
    townlist.push_back(tw);
}

void game_engine::remTown(town* tw){}

void game_engine::addBuild(building* bd){
    buildlist.push_back(bd);
    mu_lock( mtx_maplock );
        bd->install( innermap );
    mu_unlock( mtx_maplock );
}

void game_engine::remBuild(building* bd){}

float game_engine::dice(){
    return float(rand())/float(RAND_MAX);
}

unsigned int game_engine::dice(unsigned int max){
    return (float(rand())/float(RAND_MAX)) * max + 1;
}
